﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NLog;
using System.Xml;
using System.Xml.XPath;
using System.IO;

namespace FA.FlightAttendant.Plugin
{
    public class SimulatorFileWriter
    {
        private string _file;

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="file"></param>
        public SimulatorFileWriter(string file)
        {
            _file = file;
        }

        /// <summary>
        /// Add a new simulator to the file.
        /// </summary>
        /// <param name="pluginClass"></param>
        /// <param name="name"></param>
        /// <param name="settings"></param>
        public void AddSimulator(string pluginClass, string name, Dictionary<string, string> settings)
        {
            // Check for null parameters
            if (pluginClass == null || pluginClass.Trim().Length == 0)
                throw new ArgumentNullException("pluginClass");
            if (name == null || name.Trim().Length == 0)
                throw new ArgumentNullException("name");
            if (settings == null)
                settings = new Dictionary<string, string>();

            // Check if the file exists
            if (File.Exists(_file))
            {
                // Check if a simulator already has this name
                if (checkNameAlreadyExists(name))
                    throw new Exception("A simulator plugin already exists with the name '" + name + "'.");
            }
            else
            {
                CreateSimulatorsFile();
            }
            
            // Load the XML document
            XmlDocument doc = new XmlDocument();
            doc.Load(_file);

            // Locate the root <simulators> element
            XmlNodeList simulatorsNodeList = doc.GetElementsByTagName("simulators");
            if (simulatorsNodeList.Count == 0)
                throw new Exception("Could not locate <simulators> element in XML file.");
            XmlNode root = simulatorsNodeList[0];

            // Add the <simulator> node to the document
            XmlElement simulatorElement = doc.CreateElement("simulator");
            root.AppendChild(simulatorElement);

            // Add the <plugin> node
            XmlElement pluginElement = doc.CreateElement("plugin");
            pluginElement.InnerText = pluginClass;
            simulatorElement.AppendChild(pluginElement);

            // Add the <name> node
            XmlElement nameElement = doc.CreateElement("name");
            nameElement.InnerText = name;
            simulatorElement.AppendChild(nameElement);

            // Add the <settings> node
            XmlElement settingsElement = doc.CreateElement("settings");
            foreach (KeyValuePair<string, string> setting in settings)
            {
                XmlElement settingElement = doc.CreateElement("setting");

                XmlAttribute nameAttribute = doc.CreateAttribute("name");
                nameAttribute.Value = setting.Key;
                settingElement.Attributes.Append(nameAttribute);
                settingElement.InnerText = setting.Value;
                settingsElement.AppendChild(settingElement);
            }
            simulatorElement.AppendChild(settingsElement);

            // Save the document
            doc.Save(_file);
        }

        /// <summary>
        /// Edit an existing simulator in the file.
        /// </summary>
        /// <param name="originalName"></param>
        /// <param name="newPluginClass"></param>
        /// <param name="newName"></param>
        /// <param name="newSettings"></param>
        public void EditSimulator(string originalName, string newPluginClass, string newName, Dictionary<string, string> newSettings)
        {
            // Check for null parameters
            if (originalName == null || originalName.Trim().Length == 0)
                throw new ArgumentNullException("originalName");
            if (newPluginClass == null || newPluginClass.Trim().Length == 0)
                throw new ArgumentNullException("newPluginClass");
            if (newName == null || newName.Trim().Length == 0)
                throw new ArgumentNullException("newName");
            if (newSettings == null)
                newSettings = new Dictionary<string, string>();

            // Check if the file exists
            if (File.Exists(_file))
            {
                // If the name has changed check if the new one already exists
                if (!originalName.Trim().Equals(newName.Trim()) && checkNameAlreadyExists(newName))
                    throw new Exception("A simulator plugin already exists with the name '" + newName + "'.");
            }
            else
            {
                CreateSimulatorsFile();
            }

            // Remove the old entry
            RemoveSimulator(originalName);

            // Add the new entry
            AddSimulator(newPluginClass, newName, newSettings);
        }

        /// <summary>
        /// Remove an existing simulator from the file.
        /// </summary>
        /// <param name="sim"></param>
        public void RemoveSimulator(string name)
        {
            // Check for null parameters
            if (name == null || name.Trim().Length == 0)
                throw new ArgumentNullException("name");

            // Check if the file exists
            if (!File.Exists(_file))
                return;

            // Load the XML document
            XmlDocument doc = new XmlDocument();
            doc.Load(_file);
            XPathNavigator navigator = doc.CreateNavigator();

            XPathNavigator nodeToDelete = null;

            // Locate the node to delete
            XPathNodeIterator iterator = navigator.Select("/simulators/simulator");
            while (iterator.MoveNext())
            {
                XPathNodeIterator nameIterator = iterator.Current.SelectChildren("name", String.Empty);
                while (nameIterator.MoveNext())
                {
                    // Check if the simulator name matches
                    if (nameIterator.Current.Value != null && name.Trim().Equals(nameIterator.Current.Value.Trim()))
                    {
                        // Delete the simulator entry
                        nodeToDelete = iterator.Current;
                        break;
                    }
                }

                if (nodeToDelete != null)
                    break;
            }

            nodeToDelete.DeleteSelf();

            doc.Save(_file);
        }

        /// <summary>
        /// Create the simulators XML file.
        /// </summary>
        public void CreateSimulatorsFile()
        {
            using (XmlWriter writer = XmlWriter.Create(_file))
            {
                writer.WriteStartDocument();
                writer.WriteStartElement("simulators");
                writer.WriteEndElement();
                writer.WriteEndDocument();
            }
        }

        /// <summary>
        /// Check if a simulator already exists with this name.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        private bool checkNameAlreadyExists(string name)
        {
            // Check the argument
            if (String.IsNullOrEmpty(name))
                throw new ArgumentNullException("name");

            // Check if the file exists
            if (!File.Exists(_file))
                return false;

            bool nameExists = false;

            // Load the XML document
            XmlDocument doc = new XmlDocument();
            doc.Load(_file);
            XPathNavigator navigator = doc.CreateNavigator();

            // Iterate through each simuluator's name
            XPathNodeIterator iterator = navigator.Select("/simulators/simulator/name");
            while (iterator.MoveNext())
            {
                if (iterator.Current.Value != null && name.Trim().Equals(iterator.Current.Value.Trim()))
                {
                    nameExists = true;
                    break;
                }
            }

            return nameExists;
        }
    }
}